/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
// espefuse.py -p COM3 set_flash_voltage 3.3V
// espefuse.py -p COM7 set_flash_voltage 3.3V , BURN: to program flash voltage selection efuses to 3.3V to release the gpio12
// idf.py -p COM7 build flash monitor
// idf.py -p COM3 build flash monitor
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/stream_buffer.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_mac.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "addr_from_stdin.h"

#include "cJSON.h"
#include "driver/uart.h"

#include "timer_x.h"
#include "wifi_x.h"
// #include "ad768x.h"
#include "gpio_x.h"
// #include "ads1299.h"
#include "ads1299_driver.h"

#include "m_config.h"
#include "spi_x.h"
#include "adc_x.h"


// #define USE_JSON 1
uint8_t fp_mac[6] = {0}; // factory_programmed_mac[6];

static TaskHandle_t xRead_ads_task = NULL;

// uint16_t c = 0;
// static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
// {
//     xTaskNotifyGive(timer_task);
//     return pdTRUE;
// }

static const char *TAG = "main - ";

typedef enum
{
    not_valid_cmd = 0,
    request_info,
    request_mac,
    request_battery,
    btn_evt,
    test_status,
    start_recording,
    stop_recording
} m_cmd_t;

#define CMD_QUEUE_LENGTH 8
#define CMD_QUEUE_ITEM_SIZE sizeof(m_cmd_t)

// #define uart_send_QUEUE_LENGTH 32
// #define uart_send_QUEUE_ITEM_SIZE sizeof(send_message_t)

// static QueueHandle_t timer_queue;
static QueueHandle_t cmd_queue;
// static QueueHandle_t uart_send_queue;
char gl_mac_str[] = "00:00:00:00:00:00";
char gl_bat_str[4] = "99"; // convert the battery level to a string, then transfer it

#define DATA_QUEUE_LENGTH 8
// #define DATA_QUEUE_ITEM_SIZE sizeof(data_pkg_t)
// #define DATA_QUEUE_ITEM_SIZE sizeof(data_ads_pkg_t)
#define DATA_QUEUE_ITEM_SIZE ADS_DATA_PACKET_LEN

static QueueHandle_t data_queue;

#define cmd_key_mac_str "mac"
#define cmd_key_str "cmd"
#define cmd_request_str "request"
#define test_key_str "test"

#define test_data_str "status"
#define start_recording_str "start recording"
#define stop_recording_str "stop recording"

#define data_pkg_key_str_pkgNum "pkgNum"
#define data_pkg_key_str_sampleNum "sampleNum"
#define data_pkg_key_str_chNum "chNum"
#define data_pkg_key_str_data "data"

#define cmd_request_info "dev info"
#define cmd_request_mac "MAC"
#define cmd_request_bat "bat"



const char info_str[] = {"U8embeded v1.1.3 \r\n"};
const char heart_beat_pkg[] = {"{\"status\":\"ok\"} \r\n"};//test readline in TCP server
// const char heart_beat_pkg[] = {"{\"status\":\"ok\"}"};

#define data_pkg_chNum 8
#define data_pkg_sampleNum 10

#define EX_UART_NUM UART_NUM_0
#define PATTERN_CHR_NUM (3) /*!< Set the number of consecutive and identical characters received by receiver which defines a UART pattern*/
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)
static QueueHandle_t uart0_queue;
//  - ----------------------- gpio

#define GPIO_BTN_NUM GPIO_NUM_5
#define ESP_INTR_FLAG_DEFAULT 0

// static QueueHandle_t gpio_evt_queue = NULL;
TimerHandle_t xTimers_btn_led;

// extern char gl_ssid_str[32];
// extern char gl_pw_str[64];
// extern char gl_ip_str[16];
// extern char gl_port_str[8];

#define new_ip_key_str "hostIP"
#define ssid_key_str "ssid"
#define pw_key_str "pw"
#define port_key_str "port"

#define tcp_ack_key_str "ack"
#define tcp_ack_data_str "ok"

const char nvs_ip_str[] = "hostIP"; // key
const char nvs_ssid_str[] = "ssid";
const char nvs_pw_str[] = "pw";
const char nvs_port_str[] = "port";

nvs_handle_t my_nvs_handler;

#define cmd_key_channel_num_str "chn" // number of channel
#define cmd_key_packet_num_str "pkn"

#define data_key_eeg_str "eeg"
#define number_of_1_channel_str "1"


static uint32_t ads1299_callback_counter = 0;

#ifdef USE_SPI_DATA_OUT
    uint32_t counter_send_spi = 0;
#endif

void read_ads_task(void *pParam)
{
    // data_ads_pkg_t ads_data_pkg = {0};
    // ads_data_pkg.header[0]=0xA5;
    // ads_data_pkg.header[1]=0xA5;
    // ads_data_pkg.tail[0]=0x5A;
    // ads_data_pkg.tail[1]=0x5A;

    // data_ads_buff[0] = 0xA5;
    // data_ads_buff[1] = 0xA5;
    // data_ads_buff[ads_pkg_len-2] = 0x5A;
    // data_ads_buff[ads_pkg_len-1] = 0x5A;
    // const int data_ads_buff_offset = 4;

	DATA_ADS_INIT(ads_data_pkg);
    ads_dp_ic_t dp_ic = {0};
    // ads_sample_int_offset_in_data_packet = 0;
    dp_ic.ads_sample_counter = 0;
    // dp_ic.ads_sample_int_offset_in_data_packet = 0;
    uint8_t rx_stauts[3*CHIP_NUM] = {0};
    
    while (1)
    {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        // test_ads();

        // //check the code carefully to make sure that the first data in a packet is the first channel data,
        // //so that the data of different channnels sit in the correct order !   

        // ads1299_drv_get_data(&ads_data_pkg.data[ads_data_sample_len*ads_sample_counter]);

        // ads_get_data(&d.data[c],0);
        // ads_get_data(&d.data[c+27],1);
        // ads_get_data(&d.data[c+54],2);

        // ads1299_drv_get_data_int_array(&ads_data_pkg.data[ads_data_sample_len_in_int*ads_sample_counter]);
        
        // int tmp = data_ads_buff_offset+ads_data_sample_len_in_int*ads_sample_counter;
        // ESP_LOGI(TAG, "tmp : %d",tmp);

#ifdef pkg_use_int_data   
        // get one sample, and put data into the right locatio in the data_packet.data
        // int index_u8 = dp_ic.ads_sample_int_offset_in_data_packet+ads_int_data_len_in_byte*dp_ic.ads_sample_counter;
        // int index_u8 = ;

        ads1299_drv_get_data_int_array((int*)&ads_data_pkg.data[ads_int_data_len_in_byte*dp_ic.ads_sample_counter],rx_stauts);// the rx_status will be overwriten 10 times for every use
        dp_ic.ads_sample_counter++;


        
#endif

#ifdef pkg_use_raw_data 
        ads1299_drv_get_data(&ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter]);
        // ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter]=0x01;
        // ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter+1]=0x02;
        // ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter+2]=0x03;
        // for(int i=3;i<6;i++){
        //     ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter+i]=0x05;
        // }

        dp_ic.ads_sample_counter++;
        // ESP_LOGI(TAG, "- %x, %x, %x, %x",ads_data_pkg.data[4],ads_data_pkg.data[36],ads_data_pkg.data[72],ads_data_pkg.data[108]);
#endif

#ifdef USE_SPI_DATA_OUT
        spi_data_out_send(&data_ads_buff[index_u8],RX_BUFF_LEN_INT_ARRAY);


        /*test spi send data
        if(counter_send_spi<10){
            counter_send_spi++;
        }else{
            counter_send_spi = 0;
            // data_ads_buff_test[0]= 0x51;
            // data_ads_buff_test[1]= 0xFE;
            // data_ads_buff_test[2]= 0x10;
            // data_ads_buff_test[3]= 0x1D;
            // spi_data_out_send(&data_ads_buff[0],RX_BUFF_LEN_INT_ARRAY);
            spi_data_out_send(&data_ads_buff[data_ads_buff_offset],RX_BUFF_LEN_INT_ARRAY);
            // ESP_LOGI(TAG, "spi_data_out_send");
        }              
        */
#endif
        // ads_sample_counter++;

        if(dp_ic.ads_sample_counter<sample_num_in_pkg){
            continue;
        }
        dp_ic.ads_sample_counter = 0;
        ads_data_pkg.pkg_num++;        

        // ESP_LOG_BUFFER_HEX(TAG, rx_stauts, 3);
        
        // bits[i] = byte & (mask << i);

        // ads_sample_counter=0;
        // memcpy(&data_ads_buff[2],(int8_t*)&(ads_data_pkg.pkg_num),2);

        // ESP_LOGI(TAG, ": %x %x",d.data[0],d.data[1]);
        // ESP_LOG_BUFFER_HEX(TAG, &data_ads_buff[128], 8);

        // try not sending here, use a queue, and drop some packet if too much data wait to send;
        // and this way provide a way to check if too much data wait to send;
        // this will cause some more copy operation!
        // if (wifi_send_x((char *)&d, DATA_NUM_PKG_LEN_IN_BYTE_ADS) < 0)
        // {
        //     ESP_LOGI(TAG, "send error");
        // }
        // for(int i=0;i<270;i++){
        //     ads_data_pkg.data[i] = i;
        // }
        // BaseType_t r = xQueueSendToBack(data_queue, data_ads_buff, 0);
        BaseType_t r = xQueueSendToBack(data_queue, &ads_data_pkg, 0);
 
        // ESP_LOG_BUFFER_HEXDUMP(TAG, &ads_data_pkg, 16, ESP_LOG_INFO);

        if (r == errQUEUE_FULL){
            ESP_LOGI(TAG, "data queue full ! ");
        }
    }
}

void check_status_task(void *pParam){
    uint32_t counter_old = 0;
    uint32_t dif = 0;
    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        wifi_status_x_t m_status = get_wifi_status();
        if (m_status == WIFI_OK){
            // power_led_on();
            keep_power_led_on();
            // ESP_LOGI(TAG, "WIFI_OK");
        }else if (m_status == WIFI_BAD)
        {
            power_led_toggle();
            // ESP_LOGI(TAG, "WIFI_BAD");
        }

        ads1299_rec_status_t m_ads1299_status = get_rec_status();
        if (m_ads1299_status == REC_ON){
            rec_led_toggle();
            dif = (ads1299_callback_counter - counter_old);
            ESP_LOGI(TAG, "   %ld ", dif);
            counter_old = ads1299_callback_counter;

            // ESP_LOGI(TAG, "REC_ON");
        }
        if (m_ads1299_status == REC_OFF){
            rec_led_off();
            // ESP_LOGI(TAG, "REC_OFF");
        }       
    }
    vTaskDelete(NULL);
}

void m_deal_cmd_task(void *pParam)
{
    // uint8_t write_char_data[1] = {0xFF};

    while (1)
    {
        m_cmd_t c = {0};
        if (xQueueReceive(cmd_queue, &c, portMAX_DELAY) == pdPASS)
        {
            // if (c == unbind)
            // {
            //     esp_err_t ret = nvs_erase_key(my_nvs_handler, nvs_bound_mac_str);
            //     if (ret != ESP_ERR_NVS_NOT_FOUND)
            //     {
            //         ESP_ERROR_CHECK(ret);
            //     }
            // }
            // if (c == flash_led)
            // {
            //     write_char_data[0] = 1;
            //     // printf("flash_led \n");
            // }
            if (c == request_info)
            {
                uart_write_bytes(EX_UART_NUM, info_str, sizeof(info_str));
                
                cJSON *root = cJSON_CreateObject();
                cJSON_AddStringToObject(root, cmd_request_info, info_str);
 
                char *p_json_str;
                p_json_str = (char *)malloc(1024);
                p_json_str = cJSON_PrintUnformatted(root);

                if (wifi_send_x(p_json_str, strlen(p_json_str)) < 0){
                    ESP_LOGI(TAG, "send error");
                }

                cJSON_Delete(root);
                free(p_json_str);
            }
            if (c == request_mac)
            {
                // uart_write_bytes(EX_UART_NUM, info_str, sizeof(info_str));
                
                cJSON *root = cJSON_CreateObject();
                cJSON_AddStringToObject(root, cmd_request_mac, gl_mac_str);
 
                char *p_json_str;
                p_json_str = (char *)malloc(1024);
                p_json_str = cJSON_PrintUnformatted(root);

                if (wifi_send_x(p_json_str, strlen(p_json_str)) < 0){
                    ESP_LOGI(TAG, "send error");
                }

                cJSON_Delete(root);
                free(p_json_str);
            }            
            if (c == request_battery)
            {
                // uart_write_bytes(EX_UART_NUM, info_str, sizeof(info_str));
                
                cJSON *root = cJSON_CreateObject();
                cJSON_AddStringToObject(root, cmd_request_bat, gl_bat_str);

                char *p_json_str;
                p_json_str = (char *)malloc(1024);
                p_json_str = cJSON_PrintUnformatted(root);

                if (wifi_send_x(p_json_str, strlen(p_json_str)) < 0){
                    ESP_LOGI(TAG, "send error");
                }

                cJSON_Delete(root);
                free(p_json_str);
            }

            if (c == btn_evt)
            {
                ESP_LOGI(TAG, "btn evt");
                char a[] = "btn evt\r\n";
                wifi_send_x(a,sizeof(a));
            }

            if (c == test_status)
            {
                cJSON *root = cJSON_CreateObject();
                cJSON_AddStringToObject(root, test_data_str, "ok");
 
                char *p_json_str;
                p_json_str = (char *)malloc(1024);
                p_json_str = cJSON_PrintUnformatted(root);

                if (wifi_send_x(p_json_str, strlen(p_json_str)) < 0){
                    ESP_LOGI(TAG, "send error");
                }

                cJSON_Delete(root);
                free(p_json_str);
                ESP_LOGI(TAG, "test_status cmd complete");

            }
            if (c == start_recording)
            {
                ads1299_dev_start_recording();
            }
            if (c == stop_recording)
            {
                ads1299_dev_stop_recording();
            }
        }
    }
}

static void get_json_cmd(uint8_t *p_data, uint16_t len){
    // please note that the programm only handle the case that we have one json in one uart-event,
    // I used free to release the node, so if we have one valid json-cmd, then I will excute it, then release the node,
    // then the whole json would break.
    // so it can not handler a uart-event with multiple json in it.

    // later I did not use cjson_free, instead, I used cjson_delete in the proper position, but still, handle only one json in one
    // uart event

    cJSON *cjson_root = NULL;
    cjson_root = cJSON_ParseWithLength((char *)p_data, len);

    // uart_write_bytes(EX_UART_NUM, p_data, len);// print p_data as ascii, for example :4f 4b 0a will be "OK"

    if (cjson_root == NULL)
    {
        cJSON_Delete(cjson_root);
        return;
    }
        
    cJSON *cmd_ack = cJSON_GetObjectItem(cjson_root, tcp_ack_key_str);
    if (cmd_ack != NULL)
    {
        if(strcmp(cmd_ack->valuestring, tcp_ack_data_str) ==0){
            ESP_LOGI(TAG, "cmd_ack ok");
        }else{
            // deal with other cmds
        }

        cJSON_Delete(cjson_root);
        return;
    }

    cJSON *cmd_new_ip = cJSON_GetObjectItem(cjson_root, new_ip_key_str);
    if (cmd_new_ip != NULL)
    {
        ESP_ERROR_CHECK(nvs_set_str(my_nvs_handler, nvs_ip_str, cmd_new_ip->valuestring));
        ESP_ERROR_CHECK(nvs_commit(my_nvs_handler));
        ESP_LOGI(TAG, "ip setting : %s", cmd_new_ip->valuestring);
        cJSON_Delete(cjson_root);
        return;
    }
    cJSON *cmd_ssid = cJSON_GetObjectItem(cjson_root, ssid_key_str);
    if (cmd_ssid != NULL)
    {
        ESP_ERROR_CHECK(nvs_set_str(my_nvs_handler, nvs_ssid_str, cmd_ssid->valuestring));
        ESP_ERROR_CHECK(nvs_commit(my_nvs_handler));
        ESP_LOGI(TAG, "ssid setting : %s", cmd_ssid->valuestring);

        cJSON_Delete(cjson_root);
        return;
    }
    cJSON *cmd_pw = cJSON_GetObjectItem(cjson_root, pw_key_str);
    if (cmd_pw != NULL)
    {
        ESP_ERROR_CHECK(nvs_set_str(my_nvs_handler, nvs_pw_str, cmd_pw->valuestring));
        ESP_ERROR_CHECK(nvs_commit(my_nvs_handler));
        ESP_LOGI(TAG, "pw setting : ********");

        cJSON_Delete(cjson_root);
        return;
    }
    cJSON *cmd_port = cJSON_GetObjectItem(cjson_root, port_key_str);
    if (cmd_port != NULL)
    {
        ESP_ERROR_CHECK(nvs_set_str(my_nvs_handler, nvs_port_str, cmd_port->valuestring));
        ESP_ERROR_CHECK(nvs_commit(my_nvs_handler));
        ESP_LOGI(TAG, "port setting : %s", cmd_port->valuestring);

        cJSON_Delete(cjson_root);
        return;
    }

    cJSON *request_json = cJSON_GetObjectItem(cjson_root, cmd_request_str);
    if (request_json != NULL)
    {
        m_cmd_t c = not_valid_cmd;
        if (memcmp(cmd_request_info, request_json->valuestring, sizeof(cmd_request_info)) == 0)
        {
            c = request_info;
        }
        if (memcmp(cmd_request_bat, request_json->valuestring, sizeof(cmd_request_bat)) == 0)
        {
            c = request_battery;
        }
        if (memcmp(cmd_request_mac, request_json->valuestring, sizeof(cmd_request_mac)) == 0)
        {
            c = request_mac;
        }
        if (c != not_valid_cmd)
        {
            if (xQueueSendToBack(cmd_queue, &c, 0) != pdPASS)
            {
                // printf("error here \n");
            }
        }
        else
        {
            // printf("cmp failed\n");
        }
        // cJSON_free(request_json);
        // cJSON_free(cjson_root);
        cJSON_Delete(cjson_root);

        return;
    }  

    cJSON *test_status_json = cJSON_GetObjectItem(cjson_root, test_key_str);
    if (test_status_json != NULL)
    {
        m_cmd_t c = not_valid_cmd;
        if (memcmp(test_data_str, test_status_json->valuestring, sizeof(test_data_str)) == 0)
        {
            c = test_status;
        }
        if (c != not_valid_cmd)
        {
            if (xQueueSendToBack(cmd_queue, &c, 0) != pdPASS)
            {
                // printf("error here \n");
            }
        }
        else
        {
            // printf("cmp failed\n");
        }
        // cJSON_free(request_json);
        // cJSON_free(cjson_root);
        cJSON_Delete(cjson_root);

        return;
    }

    cJSON *cmd = cJSON_GetObjectItem(cjson_root, cmd_key_str);
    if (cmd != NULL)
    {
        m_cmd_t c = not_valid_cmd;
        if (memcmp(stop_recording_str, cmd->valuestring, sizeof(stop_recording_str)) == 0)
        {
            c = stop_recording;
        }        
        if(memcmp(start_recording_str, cmd->valuestring, sizeof(start_recording_str)) == 0){
            c =  start_recording;
        }
        if (c != not_valid_cmd)
        {
            if (xQueueSendToBack(cmd_queue, &c, 0) != pdPASS)
            {
                // printf("error here \n");
            }
        }
        else
        {
            // printf("cmp failed\n");
        }
        // cJSON_free(request_json);
        // cJSON_free(cjson_root);
        cJSON_Delete(cjson_root);

        return;
    }

    // other cmd we do not define here,just ignore it.
    cJSON_Delete(cjson_root);
}

static void wifi_client_rcv_task(void *pvParameters)
{
    char rx_buffer[256];
    // wifi_waitfor_connection();
    while (1)
    {
        vTaskDelay(10 / portTICK_PERIOD_MS);

        int len = wifi_recv_x(rx_buffer, sizeof(rx_buffer) - 1);
        
        // ESP_LOGI(TAG, "len : %d",len);

        if (len>0){
            ESP_LOG_BUFFER_HEXDUMP(TAG, rx_buffer, len, ESP_LOG_INFO);

            get_json_cmd((uint8_t*)rx_buffer, len);
        }

        // ESP_LOGI(TAG, "Received nothing, retry");
    }
    vTaskDelete(NULL);
} 


/* wifi_client_heart_beat

*/
static void wifi_client_heart_beat_task(void *pvParameters){
    
    // uint8_t loff_rx[CHIP_NUM*2] = {0};

    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);

        if (get_rec_status() == REC_ON){
            continue;
        }

        if (wifi_send_x(heart_beat_pkg, strlen(heart_beat_pkg)) < 0){
            // ESP_LOGI(TAG, "send error");
        }

        // ads1299_drv_get_leadoff_status_rx(loff_rx);

        // if (wifi_send_x((char*)loff_rx, CHIP_NUM*2) < 0){
        //     // ESP_LOGI(TAG, "send error");
        // }
    }
    vTaskDelete(NULL);
}

void send_data_though_wifi_task(void *pParam)
{
    // data_pkg_t d = {0};
    // data_ads_pkg_t d = {0};
    // uint8_t buff_for_queue[ads_pkg_len] = {0};

	DATA_ADS_INIT(ads_data_pkg_buffer);

    while (1)
    {
        if (xQueueReceive(data_queue, &ads_data_pkg_buffer, portMAX_DELAY) != pdPASS)
        {
            ESP_LOGI(TAG, "something wrong, need check...1...");
            continue;
        }
        // if (xQueueReceive(data_queue, &d, portMAX_DELAY) != pdPASS)
        // {
        //     ESP_LOGI(TAG, "something wrong, need check...1...");
        //     continue;
        // }
// #ifdef pkg_use_raw_data 
//         // use binary - low energy
//         // if (wifi_send_x((char *)&d, DATA_NUM_PKG_LEN_IN_BYTE) < 0)
//         // if (wifi_send_x((char *)&d, DATA_NUM_PKG_LEN_IN_BYTE_ADS) < 0)
//         // if (wifi_send_x((char *)&d, ads_pkg_len) < 0)        
//         if (wifi_send_x((char *)&d, ads_pkg_len) < 0)        
//         {            
//             // comment 20240417 tmp
//             ESP_LOGI(TAG, "send error");
//         }
// #endif

#ifdef pkg_use_json 

        cJSON *root = cJSON_CreateObject();
        cJSON_AddNumberToObject(root, data_pkg_key_str_pkgNum, d.pkg_num);
        cJSON_AddNumberToObject(root, data_pkg_key_str_sampleNum, data_pkg_sampleNum);
        cJSON_AddNumberToObject(root, data_pkg_key_str_chNum, data_pkg_chNum);

        cJSON *p_data_eeg_Array = cJSON_CreateIntArray(d.data, ads_pkg_data_int_len);
        cJSON_AddItemToObject(root, data_key_eeg_str, p_data_eeg_Array);

        char *p_json_str;
        p_json_str = (char *)malloc(1024*8);
        p_json_str = cJSON_PrintUnformatted(root);

        if (wifi_send_x(p_json_str, strlen(p_json_str)) < 0){
            ESP_LOGI(TAG, "send error");
        }

        cJSON_Delete(root);
        free(p_json_str);
        // cJSON_free(root);
#else
        /* use pkg_use_raw_data or pkg_use_int_data*/
        if (wifi_send_x((char *)&ads_data_pkg_buffer, ADS_DATA_PACKET_LEN) < 0)        
        {            
            ESP_LOG_BUFFER_HEX(TAG, (char *)&ads_data_pkg_buffer, 8);
            ESP_LOGI(TAG, "send error");
        }

#endif

        // ESP_LOGI(TAG, "pkg   : %d",d.pkg_num);
        // UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
        // ESP_LOGI(TAG, "space available   : %d",uxHighWaterMark);
    }
}

static void wifi_init_task(void *pvParameters){
    wifi_x_init();
    get_mac_str(gl_mac_str);

    xTaskCreate(wifi_client_rcv_task, "wifi_client_rcv", 1024*8 , NULL, 4, NULL);

    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);  
    }
    vTaskDelete(NULL);
}

static void deal_with_uart_data(uint8_t *p_data, uint16_t len)
{
    get_json_cmd(p_data, len);
}

static void uart_event_task(void *pvParameters)
{
    uart_event_t event;
    size_t buffered_size;
    uint8_t *dtmp = (uint8_t *)malloc(RD_BUF_SIZE);
    for (;;)
    {
        // Waiting for UART event.
        if (xQueueReceive(uart0_queue, (void *)&event, (TickType_t)portMAX_DELAY))
        {
            bzero(dtmp, RD_BUF_SIZE);
            // ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
            switch (event.type)
            {
            // Event of UART receving data
            /*We'd better handler data event fast, there would be much more data events than
            other types of events. If we take too much time on data event, the queue might
            be full.*/
            case UART_DATA:
                // uart_write_bytes(EX_UART_NUM, (const char *)str, sizeof(str));

                // ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                // ESP_LOGI(TAG, "[DATA EVT]:");
                // uart_write_bytes(EX_UART_NUM, (const char *)dtmp, event.size);
                deal_with_uart_data(dtmp, event.size);

                break;
            // Event of HW FIFO overflow detected
            case UART_FIFO_OVF:
                ESP_LOGI(TAG, "hw fifo overflow");
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart0_queue);
                break;
            // Event of UART ring buffer full
            case UART_BUFFER_FULL:
                ESP_LOGI(TAG, "ring buffer full");
                // If buffer full happened, you should consider increasing your buffer size
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart0_queue);
                break;
            // Event of UART RX break detected
            case UART_BREAK:
                ESP_LOGI(TAG, "uart rx break");
                break;
            // Event of UART parity check error
            case UART_PARITY_ERR:
                ESP_LOGI(TAG, "uart parity error");
                break;
            // Event of UART frame error
            case UART_FRAME_ERR:
                ESP_LOGI(TAG, "uart frame error");
                break;
            // UART_PATTERN_DET
            case UART_PATTERN_DET:
                uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                int pos = uart_pattern_pop_pos(EX_UART_NUM);
                ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
                if (pos == -1)
                {
                    // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                    // record the position. We should set a larger queue size.
                    // As an example, we directly flush the rx buffer here.
                    uart_flush_input(EX_UART_NUM);
                }
                else
                {
                    uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                    uint8_t pat[PATTERN_CHR_NUM + 1];
                    memset(pat, 0, sizeof(pat));
                    uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
                    ESP_LOGI(TAG, "read data: %s", dtmp);
                    ESP_LOGI(TAG, "read pat : %s", pat);
                }
                break;
            // Others
            default:
                ESP_LOGI(TAG, "uart event type: %d", event.type);
                break;
            }
        }
    }
    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}

esp_err_t nvs_check_get_str(nvs_handle_t c_handle, const char *key, char *out_value, int len)
{
    size_t required_size;

    esp_err_t ret = nvs_get_str(c_handle, key, NULL, &required_size);
    switch (ret)
    {
    case ESP_OK:
        if (required_size > len)
        {
            ESP_LOGE(TAG, "the string too long");
            break;
        }
        else
        {
            memset(out_value, 0, len);
            nvs_get_str(my_nvs_handler, key, out_value, &required_size);

            ESP_LOGI(TAG, "read suc ---------  %s", out_value);

            return ret;
            // char *s = malloc(required_size);
            // Example (without error checking) of using nvs_get_str to get a string into dynamic array:
            // ret = nvs_get_str(my_nvs_handler, nvs_ip_str, s, &required_size);
            // memcpy(gl_ip_str, s, required_size);
        }
    case ESP_ERR_NVS_NOT_FOUND:
        // printf("The value is not initialized yet!\n");
        break;
    default:
        // printf("Error (%s) reading!\n", esp_err_to_name(ret));
    }
    return ret;
}

void vTimerBtnCallback(TimerHandle_t xTimer)
{
    if (get_btn_level(GPIO_BTN_NUM) == 0)
    {
        return;
    }
    m_cmd_t c = btn_evt;

    if (xQueueSendToBack(cmd_queue, &c, 0) != pdPASS)
    {
        // printf("error here \n");
    }

    // if(led_flicker_count==0){

    //     return;
    // }
    // led_flicker_count--;
    // set_led_level(1); // turn off the led
    xTimerStopFromISR(xTimers_btn_led, 0);

    // xTaskNotifyGive(xRead_ads_task);
}

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    if (xTimers_btn_led != NULL)
    {
        xTimerStart(xTimers_btn_led, 0);
        // set_led_level(0); // light up the led
        // ESP_LOGI(TAG, " gpio_isr_handler ");
    }
    // led_toggle();

    // if (gpio_num==GPIO_BTN_NUM)
    // xTaskNotifyGive(xRead_ads_task);
    // xTaskNotifyGive(timer_task);
}

void ads1299_drv_callback(uint8_t * p_data, uint16_t len){
    ads1299_callback_counter++;
    xTaskNotifyGive(xRead_ads_task);
}

// static int adc_raw;
// static int voltage;

// static esp_err_t test_adc(void){
//     const int max_retry = 10;
//     esp_err_t err = ESP_OK;
//     for (int i = 0; i < max_retry; ++i) {
//         err = adc_oneshot_read_x(&adc_raw, &voltage);

//         // adc_oneshot_read_x(&adc_raw, &voltage);       

//         if (err == ESP_OK) return ESP_OK;
//         if (err == ESP_ERR_TIMEOUT) {
//             // ADC2 正在被 Wi-Fi/RTC 占用，等待再试
//             vTaskDelay(pdMS_TO_TICKS(10));
//             continue;
//         }
//         // 其他错误直接返回
//     }
    
//     return err;

// }

char buff_task_list[512] = {0};
void app_main(void)
{
    // ------- flash, mac, led, btn, spi2(往外传输数据用的spi)

    ESP_ERROR_CHECK(nvs_flash_init());
    esp_err_t ret = nvs_open("storage", NVS_READWRITE, &my_nvs_handler);
    if (ret != ESP_OK)
    {
        // printf("Error (%s) opening NVS handle!\n", esp_err_to_name(ret));
    }
    else
    {
        if (nvs_check_get_str(my_nvs_handler, (const char *)nvs_ip_str, gl_ip_str, sizeof(gl_ip_str)) != ESP_OK)
        {
            ESP_LOGI(TAG, "%s not set yet", nvs_ip_str);
        }
        if (nvs_check_get_str(my_nvs_handler, (const char *)nvs_port_str, gl_port_str, sizeof(gl_port_str)) != ESP_OK)
        {
            ESP_LOGI(TAG, "%s not set yet", nvs_port_str);
        }
        if (nvs_check_get_str(my_nvs_handler, (const char *)nvs_ssid_str, gl_ssid_str, sizeof(gl_ssid_str)) != ESP_OK)
        {
            ESP_LOGI(TAG, "%s not set yet", nvs_ssid_str);
        }
        if (nvs_check_get_str(my_nvs_handler, (const char *)nvs_pw_str, gl_pw_str, sizeof(gl_pw_str)) != ESP_OK)
        {
            ESP_LOGI(TAG, "%s not set yet", nvs_pw_str);
        }
    }

    esp_efuse_mac_get_default(fp_mac);
    ESP_LOG_BUFFER_HEX(TAG, fp_mac, 6);

    led_init();

    power_led_on();
    lowbattery_led_off();
    rec_led_off();

    btn_init(GPIO_BTN_NUM);    
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(GPIO_BTN_NUM, gpio_isr_handler, (void *)GPIO_BTN_NUM);
    xTimers_btn_led = xTimerCreate("Timer", 500 / portTICK_PERIOD_MS, pdTRUE, (void *)0, vTimerBtnCallback);
#ifdef USE_SPI_DATA_OUT
    spi2_config();
#endif

    // ------------ adc for battery level measurement 
    adc_x_init();

    // ------------ uart
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
    xTaskCreate(uart_event_task, "uart_event_task", 1024 * 2, NULL, 3, NULL);

    // ------------ cmd: btn,uart,wifi cmd
    cmd_queue = xQueueCreate(CMD_QUEUE_LENGTH, CMD_QUEUE_ITEM_SIZE);
    xTaskCreate(m_deal_cmd_task, "m_deal_cmd_task", 1024 * 2, NULL, 2, NULL);

    // ------------- data queue
    data_queue = xQueueCreate(DATA_QUEUE_LENGTH, DATA_QUEUE_ITEM_SIZE);

    // ------------- wifi init
    // note: in the wifi_client_heart_beat_task, I send something every 1s, to know if the wifi connection is OK 
    xTaskCreate(send_data_though_wifi_task, "send_data_though_wifi_task", 1024 * 8, NULL, 1, NULL);
    xTaskCreate(wifi_client_heart_beat_task, "wifi_client_heart_baet", 1024*4 , NULL, 5, NULL);
    xTaskCreate(wifi_init_task, "wifi_client_rcv", 1024*4 , NULL, 4, NULL);
    // ESP_LOG_BUFFER_HEXDUMP(TAG, gl_mac_str, 6, ESP_LOG_INFO);
    // printf("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", gl_mac_str[0], gl_mac_str[1], gl_mac_str[2], gl_mac_str[3], gl_mac_str[4], gl_mac_str[5]);

    // ------------- ads1299
    ads1299_drv_t m_ads1299_drv;
    m_ads1299_drv.callback_handler = ads1299_drv_callback;
    
    ads1299_dev_init(m_ads1299_drv);

    /* just for check, if read data in ads1299, must comment this */
    // ads1299_drv_check_id(); 

    xTaskCreate(read_ads_task, "ads_read_task", 1024 * 4, NULL, 2, &xRead_ads_task);
    
    
    // -------------- check_status 
    xTaskCreate(check_status_task, "check_status_task", 1024*2 , NULL, 5, NULL);

#ifdef START_REC_ON_POWERON
    ads1299_dev_start_recording();
#endif
        
	// DATA_ADS_INIT(ads_data_pkg);
    // ads_dp_ic_t dp_ic = {0};
    // // ads_sample_int_offset_in_data_packet = 0;
    // dp_ic.ads_sample_counter = 0;

    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);

        // ads1299_drv_get_leadoff_status();

        ESP_LOGI(TAG, "-");

        // if (test_adc()==ESP_OK){
        //     ESP_LOGI(TAG, "Raw Data: %d ;  Cali Voltage: %d mV", adc_raw, voltage);
        // }else{
        //     ESP_LOGI(TAG, "error ");
        // }

        // adc_oneshort_read_x(&adc_raw, &voltage);       

        // lowbattery_toggle();
    
        // vTaskList(buff_task_list);
        // printf("----------------------------------------------\n");
        // printf("Name        State   Priority    Stack   Num\n");
        // printf("%s\n", buff_task_list);

        // UBaseType_t uxNumberOfFreeSpaces;
        // /* How many free spaces are currently available in the queue referenced by the
        // xQueue handle? */
        // uxNumberOfFreeSpaces = uxQueueSpacesAvailable( uart_send_queue );
        // printf(" ----- : %d \n",uxNumberOfFreeSpaces);


        // ads1299_drv_get_data(&ads_data_pkg.data[ads_raw_data_len*dp_ic.ads_sample_counter]);
        // dp_ic.ads_sample_counter++;

    }
}
